/************************************************************************
* Programmer:  Philippe Gambling
*
* Course:  CSCI 4534
*
* Date:  February 19, 2009
*
* Environment:  GCC 3.2.2 running on diamond.rocks.uhcl.edu
*
* Files Included: queue.h, queue.c, node.h
*
* Purpose:  Implements a simple generic queue structure using a linked list.
*
***********************************************************************/ 

#include <stdio.h>
#include <stdlib.h>

#include "queue.h"

/*********************** Start Private Methods **************************/

int Queue_deletefront(const Queue q)
{
/************************************************************************
* Purpose:  Removes a node from the front of the queue.
*
* Parameters:  (Queue) target queue.
*
* Action:  Unlinks node from front of list,
*          deletes the node's data, and deletes the node.
*
***********************************************************************/
  if(Queue_Empty(q)) { return EEMPTY; } // There is a problem if link is already empty
  
  Node tmp = q->_phead;
  q->_phead = tmp->NextPtr;
  
  free_Node(tmp);
  
  if((q->_icount--)==0) { // Set tail to NULL if that was the last node in the queue
    q->_ptail = NULL; 
  }
  
  return SUCCESS;
}

void Queue_copyfront(const Queue q, void * dest)
{
/************************************************************************
* Purpose:  Copies data from the front element.
*
* Parameters:  (Queue) target queue.
*
* Action:  Copies data from head node to dest.
*
***********************************************************************/
  Node_CopyData(q->_phead,dest,q->_idatasize);
}
/********************* End Private Methods *****************************/


/********************* Start Public Interfaces **************************/

Queue new_Queue(size_t datasize)
{
/************************************************************************
* Purpose:  To create a new Queue
*
* Parameters:  (size_t) size of memory to allocate for each node's data
*
* Action:  Allocates and returns a new Queue.
*
***********************************************************************/
  Queue q = (Queue)malloc(sizeof(struct queue));
  if(q == NULL) { return NULL; }
  
  q->_idatasize = datasize;
  q->_phead = NULL;
  q->_ptail = NULL;
  q->_piterator = NULL;
  q->_icount = 0;
  
  return q;
}

int Queue_AddBack(const Queue q, const void *src)
{
/************************************************************************
* Purpose:  Add a new node to the back of the queue and copy data to it
*
* Parameters:  (Queue) target queue, (void *) data to copy to new node
*
* Action:  Allocates a new node, copies data to node, and links new node
*          to tail end of the queue.
*
***********************************************************************/
  Node n = new_Node(q->_idatasize, src);
  if(n == NULL) { return EMEMFAIL; }
  
  if(Queue_Empty(q)) { // First node in queue
    q->_phead = n;
  }
  else {  // Link this node to the last node
    q->_ptail->NextPtr = n;
  }
  
  q->_ptail = n;
  q->_icount++;

  return SUCCESS;
}

int Queue_RemoveFront(const Queue q, void * dest)
{
/************************************************************************
* Purpose:  Remove a node from the front of the queue and copy its data
*           to destination pointer.
*
* Parameters:  (Queue) target queue, (void *) destination for data in node.
*
* Action:  copies data to dest, deletes the node.
*
***********************************************************************/
  if(Queue_Empty(q)) { return EEMPTY; } // Don't want to do this on an empty queue
  
  Queue_copyfront(q, dest);
  return Queue_deletefront(q);
}

int Queue_GetFront(const Queue q, void * dest)
{
/************************************************************************
* Purpose:  Returns a copy of the first value in the queue
*
* Parameters:  (Queue) target queue, (void *) pointer to destination for memcpy
*
* Action:  Copes data to dest.
*
***********************************************************************/
  if(Queue_Empty(q)) { return EEMPTY; } // Don't want to do this on an empty queue
  
  Queue_copyfront(q, dest);
  return SUCCESS;
}
int Queue_Empty(const Queue q)
{
/************************************************************************
* Purpose:  Checks if Queue is empty
*
* Parameters:  (Queue) target queue
*
* Action:  Returns 1 is _icount is 0, 0 if -icount > 0
*
***********************************************************************/
  return !(q->_icount>0);
}

int Queue_Size(const Queue q)
{
/************************************************************************
* Purpose:  Returns the number of elements in the queue
*
* Parameters:  (Queue) target queue
*
* Action:  Returns _icount
*
***********************************************************************/
  return q->_icount;
}

int Queue_Clear(const Queue q)
{
/************************************************************************
* Purpose:  Clears all nodes from the queue
*
* Parameters:  (Queue) target queue
*
* Action:  Deletes all nodes, resets all pointers to null, set _icount to 0
*
***********************************************************************/
  while(!Queue_Empty(q))
  {
    Queue_deletefront(q);
  }  
  return SUCCESS;
}

void Queue_StartIterator(const Queue q)
{
/************************************************************************
* Purpose:  Resets the Queue iterator to head
*
* Parameters:  (Queue) target queue
*
* Action:  Sets _piterator equal to _phead
*
***********************************************************************/
  q->_piterator = q->_phead;
}

int Queue_NextNode(const Queue q, void * dest)
{
/************************************************************************
* Purpose:  Copies the data from current node to dest and moves iterator pointer
*
* Parameters:  (Queue) target queue, (void *) pointer to destination for memcpy
*
* Action:  Copies data from current node pointed to by _piterator and moves
*          _piterator to next node. Return EEND when end of queue reached,
*          SUCCESS otherwise
*
***********************************************************************/
  if(q->_piterator == NULL) { return EEND; } // Reached end of Queue
  
  Node_CopyData(q->_piterator, dest, q->_idatasize);
  q->_piterator = q->_piterator->NextPtr;
  return SUCCESS;
}

void free_Queue(Queue q)
{
/************************************************************************
* Purpose:  Release all memory associated with the Queue
*
* Parameters:  (Queue) target queue
*
* Action:  Frees the memory allocated by each node's data and 
*          the nodes themselves.
*
***********************************************************************/
  Queue_Clear(q);
  free(q);
}
/********************* End Public Interfaces **************************/

/********************* Helper Functions *******************************/


/************************************************************************
* Purpose:  Prints all the nodes in the queue as type
*
* Parameters:  (Queue) target queue
*
* Action:  Prints the data in each node as type passed to macro
*
***********************************************************************/
#define QUEUE_PRINT_DEF(type,format) \
  void Queue_##type##Print(const Queue q) \
  { \
		type i = 0; \
		int count = 0; \
		\
		Queue_StartIterator(q); \
		printf("[ "); \
		while(Queue_NextNode(q,&i)!=EEND && count<Queue_Size(q)) \
		{ \
		  printf(#format " ",i); \
		  count++; \
		} \
		if(count==0) { \
		  printf("Empty Queue"); \
		} \
		printf("]\n"); \
  } 
QUEUE_PRINT_DEF(int,%d)
QUEUE_PRINT_DEF(char,%c)
QUEUE_PRINT_DEF(float,%f)
#undef QUEUE_PRINT_DEF
